package com.mikelouis.test.scala.impatient.chapter10_traits

import java.awt.Point
import java.io.BufferedInputStream

/**
 * Created by yuazhu on 4/17/2015.
 */
object Chapter10 extends App {
  // 1
  {
    import java.awt.geom.RectangularShape

    trait RectangleLike {
      this: RectangularShape =>
      def translate(dx: Double, dy: Double) {
        setFrame(getX + dx, getY + dy, getWidth, getHeight)
      }

      def grow(h: Double, v: Double) {
        setFrame(getX, getY, getWidth + h, getHeight + v)
      }
    }
    val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30) with RectangleLike
    egg.translate(.1, .2)
    egg.grow(.3, .4)
    println("%f %f %f %f".format(egg.getX, egg.getY, egg.getWidth, egg.getHeight))
  }

  // 2
  {
    import java.awt.Point

    class OrderedPoint private(x: Int, y: Int) extends Point(x, y) with Ordered[OrderedPoint] {
      val convertToInt = { (value: AnyVal) => value.asInstanceOf[Int]}
      val convert: Function1[AnyVal, Int] = value => value.asInstanceOf[Int]

      override def compare(that: OrderedPoint): Int = {
        if (this.x != that.x) convertToInt(this.x - that.x)
        else {
          if (this.y != that.y) convert(this.y - that.y)
          else 0
        }
      }
    }
    object OrderedPoint {
      def apply(initX: Int, initY: Int) = new OrderedPoint(initX, initY)
    }

    println(OrderedPoint(1, 2).compareTo(OrderedPoint(1, 5)))
    println(OrderedPoint(5, 3).compareTo(OrderedPoint(4, 3)))
  }

  // 3
  {

  }

  // 4
  {
    trait Logger {
      def log(msg: String)
    }

    trait CrytoLogger extends Logger {
      var key: Int = 13

      abstract override def log(msg: String) {
        super.log(encrypt(msg))
      }

      def encrypt(str: String) = str.map((c: Char) => (c + key).asInstanceOf[Char])
    }

    trait MyConsoleLogger extends Logger {
      def log(msg: String) {
        println(msg)
      }
    }

    object LoggerTest extends MyConsoleLogger with CrytoLogger {
      log("Ana are mere.")
    }
    LoggerTest
  }

  // 5
  {
    import java.beans.{PropertyChangeSupport, PropertyChangeListener, PropertyChangeEvent}

    trait PropertyChangeSupportLike {
      private val support = new PropertyChangeSupport(this)

      def addPropertyChangeListener(listener: PropertyChangeListener) {
        support.addPropertyChangeListener(listener)
      }

      def addPropertyChangeListener(propertyName: String, listener: PropertyChangeListener) {
        support.addPropertyChangeListener(propertyName, listener)
      }

      def fireIndexedPropertyChange(propertyName: String, index: Int, oldValue: Boolean, newValue: Boolean) {
        support.fireIndexedPropertyChange(propertyName, index, oldValue, newValue)
      }

      def fireIndexedPropertyChange(propertyName: String, index: Int, oldValue: Int, newValue: Int) {
        support.fireIndexedPropertyChange(propertyName, index, oldValue, newValue)
      }

      def fireIndexedPropertyChange(propertyName: String, index: Int, oldValue: Object, newValue: Object) {
        support.fireIndexedPropertyChange(propertyName, index, oldValue, newValue)
      }

      def firePropertyChange(evt: PropertyChangeEvent) {
        support.firePropertyChange(evt)
      }

      def firePropertyChange(propertyName: String, oldValue: Boolean, newValue: Boolean) {
        support.firePropertyChange(propertyName, oldValue, newValue)
      }

      def firePropertyChange(propertyName: String, oldValue: Int, newValue: Int) {
        support.firePropertyChange(propertyName, oldValue, newValue)
      }

      def firePropertyChange(propertyName: String, oldValue: Object, newValue: Object) {
        support.firePropertyChange(propertyName, oldValue, newValue)
      }

      def getPropertyChangeListeners: Array[PropertyChangeListener] = support.getPropertyChangeListeners

      def getPropertyChangeListeners(propertyName: String): Array[PropertyChangeListener] = support.getPropertyChangeListeners(propertyName)

      def hasListeners(propertyName: String): Boolean = support.hasListeners(propertyName)

      def removePropertyChangeListener(listener: PropertyChangeListener) = support.removePropertyChangeListener(listener)

      def removePropertyChangeListener(propertyName: String, listener: PropertyChangeListener) = support.removePropertyChangeListener(listener)
    }

    //    class BeansPoint(x: Int, y: Int, listener: () => ()) extends java.awt.Point(x, y) with PropertyChangeSupportLike {
    //      def this() = this(0, 0)
    //    }
    //
    //    val x = new BeansPoint()
  }

  // 7
  {
    trait Animal {
      val name: String

      def words: String = ""

      def say = println("%s: %s".format(name, words))
    }

    trait Endothermy
    trait Hypotermy

    trait Mammal extends Animal with Endothermy

    class Dog extends Mammal {
      val name = "Bethoven"
      override val words = "Haw-Haw"
    }

    class Cat extends Mammal {
      val name = "Pixel"
      override val words = "Meow!"

      override def say = {
        print("Cat: ")
        super.say
      }
    }

    val d = new Dog()
    val c = new Cat()

    d.say
    c.say
  }

  // 8
  {
    trait Buffering {
      this: java.io.InputStream =>

      val BUF_SIZE: Int = 5
      private val buf = new Array[Byte](BUF_SIZE)
      private var bufsize: Int = 0
      private var pos: Int = 0

      override def read(): Int = {
        if (pos >= bufsize) {
          bufsize = this.read(buf, 0, BUF_SIZE)
          if (bufsize > 0) -1
          pos = 0
        }
        pos += 1
        buf(pos - 1)
      }
    }

    //    val f = new java.io.FileInputStream("08.txt") with Buffering
    val inputStream = this.getClass.getClassLoader.getResourceAsStream("08.txt")
    val f = new BufferedInputStream(inputStream) with Buffering
    for (i <- 1 to 10) println(f.read().asInstanceOf)
  }

  // 9
  {
    trait Logger {
      def log(msg: String)
    }

    trait ConsoleLogger extends Logger {
      override def log(msg: String): Unit = println(msg)
    }

    trait Buffering extends java.io.InputStream with Logger {
      val buffIn = new BufferedInputStream(this)

      override def read(): Int = {
        val b = buffIn.read()
        if (b != -1) log("Read byte %02X (%s)".format(b, b.asInstanceOf[Char]))
        else log("Read EOF")
        b
      }
    }

    val in = this.getClass.getClassLoader.getResourceAsStream("08.txt")
    val fis = new BufferedInputStream(in) with ConsoleLogger with Buffering
    var b: Int = 0

    while ( {
      b = fis.read()
      b
    } != -1) {
      //print(b.asInstanceOf[Char])
    }
  }

  // 10
  {
    trait IterableInputStream extends java.io.InputStream with Iterable[Byte] {
      def iterator = new Iterator[Byte] {
        def hasNext = if (available > 0) true else false

        def next = read().asInstanceOf[Byte]
      }
    }

    //    val fis = new FileInputStream("ascii.txt") with IterableInputStream
    val in = this.getClass.getClassLoader.getResourceAsStream("ascii.txt")
    val fis = new BufferedInputStream(in) with IterableInputStream

    for (i <- fis) print(i.asInstanceOf[Char])
  }

}
